﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Bitcoin.IO;
using NUnit.Framework;
using Org.BouncyCastle.Utilities.Encoders;

namespace Bitcoin.Test.IO
{
    [TestFixture]
    public class BinaryWriterExtensionTest
    {
        private static readonly ChainParameters _testParams = ChainParameters.TestNet();
        private static readonly ChainParameters _prodParams = ChainParameters.ProdNet();

        // Old tests

        [Test]
        [TestCase(0UL, new byte[] { 0 })]
        [TestCase(1UL, new byte[] { 1 })]
        [TestCase(252UL, new byte[] { 252 })]
        [TestCase(253UL, new byte[] { 253, 253, 0 })]
        [TestCase(254UL, new byte[] { 253, 254, 0 })]
        [TestCase(255UL, new byte[] { 253, 255, 0 })]
        [TestCase(256UL, new byte[] { 253, 0, 1 })]
        [TestCase(65535UL, new byte[] { 253, 255, 255 })]
        [TestCase(65536UL, new byte[] { 254, 0, 0, 1, 0 })]
        [TestCase(4294967296UL, new byte[] { 255, 0, 0, 0, 0, 1, 0, 0, 0 })]
        public void TestVarInt(ulong value, byte[] expected)
        {
            var vi = new VarInt(value);
            var actual = vi.Encode();

            CollectionAssert.AreEqual(expected, actual);
        }

        //[Test]
        //public void TestTransactionOutput()
        //{
        //    var amount = 50UL;
        //    var addressHex = "fda79a24e50ff70ff42f7d89585da5bd19d9e5cc";
        //    var address = new BitcoinAddress(_testParams, Hex.Decode(addressHex));

        //    var o = new TransactionOutput(_testParams, null, amount, address);
        //    var actual = o.BitcoinSerialize();

        //    var expected = new byte[]
        //    {
        //        // Value (ulong)
        //        50, 0, 0, 0, 0, 0, 0, 0,
        //        // Length (VarInt)
        //        25,
        //        // OpDup
        //        118,
        //        // OpHash160
        //        169, 
        //        20,
        //        0xfd, 0xa7, 0x9a, 0x24, 0xe5, 0x0f, 0xf7, 0x0f, 0xf4, 0x2f, 0x7d, 0x89, 0x58, 0x5d, 0xa5, 0xbd, 0x19, 0xd9, 0xe5, 0xcc,
        //        // OpEqualVerify
        //        136,
        //        // OpCheckSig
        //        172
        //    };
        //    CollectionAssert.AreEqual(expected, actual);
        //}
            
        // New tests

        [Test]
        [TestCase(0UL, new byte[] { 0 })]
        [TestCase(1UL, new byte[] { 1 })]
        [TestCase(252UL, new byte[] { 252 })]
        [TestCase(253UL, new byte[] { 253, 253, 0 })]
        [TestCase(254UL, new byte[] { 253, 254, 0 })]
        [TestCase(255UL, new byte[] { 253, 255, 0 })]
        [TestCase(256UL, new byte[] { 253, 0, 1 })]
        [TestCase(65535UL, new byte[] { 253, 255, 255 })]
        [TestCase(65536UL, new byte[] { 254, 0, 0, 1, 0 })]
        [TestCase(4294967296UL, new byte[] { 255, 0, 0, 0, 0, 1, 0, 0, 0 })]
        public void TestWriteVarInt(ulong value, byte[] expected)
        {
            byte[] actual;
            using (var s = new MemoryStream())
            using (var w = new BinaryWriter(s))
            {
                w.WriterVarIntEncoded(value);
                actual = s.ToArray();
            }

            CollectionAssert.AreEqual(expected, actual);
        }

        [Test]
        public void TestWriteTransaction()
        {
            var amount = 50UL;
            var addressHex = "fda79a24e50ff70ff42f7d89585da5bd19d9e5cc";
            var address = new BitcoinAddress(_testParams, Hex.Decode(addressHex));

            var o = new TransactionOutput(_testParams, null, amount, address);
            byte[] actual;
            using (var s = new MemoryStream())
            using (var w = new BinaryWriter(s))
            {
                w.Write(o);
                actual = s.ToArray();
            }

            var expected = new byte[]
            {
                // Value (ulong)
                50, 0, 0, 0, 0, 0, 0, 0,
                // Length (VarInt)
                25,
                // OpDup
                118,
                // OpHash160
                169, 
                20,
                0xfd, 0xa7, 0x9a, 0x24, 0xe5, 0x0f, 0xf7, 0x0f, 0xf4, 0x2f, 0x7d, 0x89, 0x58, 0x5d, 0xa5, 0xbd, 0x19, 0xd9, 0xe5, 0xcc,
                // OpEqualVerify
                136,
                // OpCheckSig
                172
            };
            CollectionAssert.AreEqual(expected, actual);
        }

    }
}
